<?php

declare(strict_types=1);

namespace App\Middleware;

use App\Services\Admin\Permission;
use Donjan\Casbin\Enforcer;
use Hyperf\Context\Context;
use Hyperf\HttpServer\Router\Dispatched;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class PermissionMiddleware implements MiddlewareInterface
{
    public function __construct(
        protected ContainerInterface $container,
        protected Permission $permissionServices
    )
    {
    }

    /**
     * 权限认证
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $requestHandler = $request->getAttribute(Dispatched::class)->handler;
        $route = $requestHandler->route;
        preg_match('/^\/(.*?)\//', $route, $app);
        $infoName = $app[1].'Info';
        $appInfo = Context::get($infoName);

        if ($app[1] === 'shop') { // 店铺则先验证等级权限, 验证规则: [白名单]
            $roles = Enforcer::getRolesForUser((string)$appInfo['id'], 'shop_grade');
            $passThrough = false;
            foreach ($roles as $role) {
                if (Enforcer::enforce((string)$role, 'shop_grade', $route, strtoupper($request->getMethod()))) {
                    $passThrough = true;
                    break;
                }
            }
            if ($passThrough === false) {
                return power();
            }
        }

        if ($appInfo['is_admin'] == 1) { // 超管跳过验证
            return $handler->handle($request);
        }

        $permission = $this->permissionServices->where('path', $route)->first(); // 验证规则: [黑名单]
        if ($permission) {
            $roles = Enforcer::getRolesForUser((string)$appInfo['id'], $app[1]);
            foreach ($roles as $role) {
                if (Enforcer::enforce((string)$role, $app[1], $route, strtoupper($request->getMethod()))) {
                    return $handler->handle($request);
                }
            }
            return power();
        }

        return $handler->handle($request);
    }
}
